vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 1

Tag 7


Ein paar längere Beispiele

Widmen wir den letzten Tag der Woche ein paar weiteren Beispielen. Sie werden in diesem Kapitel nicht viel Neues lernen, auch Übungen und Quiz gibt es heute nicht. Betrachten Sie es als kurze Verschnaufpause, in der Sie sich vollständige Perl-Skripts ganz in Ruhe ansehen können. Insgesamt hat das Buch drei dieser Beispiellektionen (immer am Ende der Wochen), die Ihr neues Wissen zementieren sollen.

Wir besprechen heute drei Perl-Skripts:

Statistik mit verbessertem Histogramm

Verändern wir also noch einmal das Statistikprogramm, an dem wir die ganze Woche lang gearbeitet haben. Die Version von gestern erzeugte ein horizontales Histogramm, das etwa so aussah:

Haeufigkeit der einzelnen Zahlen:
1 | *****
2 | *************
3 | *******************
4 | ****************
5 | ***********
6 | ****
43 | *
62 | *

Heute wollen wir ein Diagramm mit vertikalen Balken ausgeben:

     *
     *
     *
     *
     *
     * *
     * * *
     * * *       *     *     *
     * * *   * * *     *     *
   * * * *   * * *     *     *
   * * * *   * * *     *     *
 * * * * * * * * *     *     *  *
 * * * * * * * * *  *  *  *  *  *  *  *
---------------------------------------
 1 2 3 4 5 6 7 8 9 12 23 25 34 37 39 42

Diese Diagrammform ist sehr viel schwieriger zu erstellen als die horizontale. Wir erstellen sie mit zwei verschachtelten for-Schleifen und großer Sorgfalt beim Zählen, damit auch alles an der richtigen Stelle landet.

Noch etwas ist in dieser Version von stats.pl anders: Sie holt sich die Daten aus einer Datei, anstatt sie am Prompt vom Benutzer eintippen zu lassen. Wie bei allen Skripts, die aus einer Datei lesen, müssen Sie diese Datei beim Aufruf des Skripts in der Kommandozeile angeben:

% statsfinal.pl daten.txt

In der Datei (hier daten.txt) steht jede Zahl in einer einzelnen Zeile.

Sie finden daten.txt auch auf der beiliegenden CD, wenn Sie keine Lust haben, die Zahlen selbst einzutippen.

Listing 7.1 zeigt den Code unseres endgültigen Statistikskripts. So viel, wie wir bereits damit gearbeitet haben, sollte es ihnen vertraut sein. Konzentrieren Sie sich auf die beiden Teile des Skripts, die anders sind als in der letzten Version: die Eingabeschleife, die die Daten aus der Datei liest (Zeile 14 bis 21), und den Code zur Ausgabe des neuen Histogramms (Zeile 36 bis 49).

Listing 7.1: Das Skript statsfinal.pl

1:  #!/usr/bin/perl -w
2:
3: $input = ''; # Benutzereingabe: Zahl
4: @nums = (); # Array: Nums;
5: %freq = (); # Hash: Zahl-Haeufigkeit
6: $maxfreq = 0; # hoechste Haeufigkeit
7: $count = 0; # Anzahl aller Nums
8: $sum = 0; # Summe
9: $avg = 0; # Durchschnitt
10: $med = 0; # Median
11: @keys = (); # temp Keys
12: $totalspace = 0; # gesamte Breite des Histogramms
13:
14: while (defined ($input = <>)) {
15: chomp ($input);
16: $nums[$count] = $input;
17: $freq{$input}++;
18: if ($maxfreq < $freq{$input}) { $maxfreq = $freq{$input} }
19: $count++;
20: $sum += $input;
21: }
22: @nums = sort { $a <=> $b } @nums;
23:
24: $avg = $sum / $count;
25: $med = $nums[$count /2];
26:
27: print "\nAnzahl der eingegebenen Nums: $count\n";
28: print "Summe der Nums: $sum\n";
29: print "Kleinste Zahl: $nums[0]\n";
30: print "Groesste Zahl: $nums[$#nums]\n";
31: printf("Durchschnitt: %.2f\n", $avg);
32: print "Mittelwert: $med\n\n";
33:
34: @keys = sort { $a <=> $b } keys %freq;
35:
36: for ($i = $maxfreq; $i > 0; $i--) {
37: foreach $zahl (@keys) {
38: $space = (length $zahl);
39: if ($freq{$zahl} >= $i) {
40: print( (" " x $space) . "*");
41: } else {
42: print " " x (($space) + 1);
43: }
44: if ($i == $maxfreq) { $totalspace += $space + 1; }
45: }
46: print "\n";
47: }
48: print "-" x $totalspace;
49: print "\n @keys\n";

Da Sie den <>-Operator zum Einlesen von Daten aus Dateien bereits kennen, dürften die Zeilen 14 bis 21 Sie nicht überraschen. Beachten Sie, dass wir jede Zeile der Datei (also jede Zahl) der $input-Variablen zuweisen und diesen Wert dann im gesamten Block verwenden.

Warum $input und nicht $_? Wir hätten auch $_ einsetzen können, aber viele der Anweisungen in diesem Block brauchen einen wirklichen Variablenbezug (sie greifen nicht von allein auf $_ zurück). In diesem Beispiel würde der Code durch $_ nur wenig kürzer, aber um einiges schwieriger zu lesen. Deshalb ist es hier die wohl bessere Idee, sich zugunsten der Lesbarkeit für eine eigene Variable zu entscheiden.

Je mehr Möglichkeiten ich in diesem Buch erkläre, desto mehr Möglichkeiten stehen Ihnen auch zur Auswahl - dass Perl ein besonderes Feature anbietet, bedeutet noch lange nicht, dass Sie es auch verwenden müssen. Man sollte immer abwägen zwischen sehr kurzem Code, den nur noch Perl-Experten entziffern können, und längerem, vielleicht gar weniger effizientem, aber dafür lesbarem Code. Betrachten Sie Ihr Perl-Skript als besonders gut gemacht, wenn jemand anderes es einfach durchlesen kann.

Abgesehen davon, dass die Daten hier aus einer Datei anstatt von der Standardeingabe gelesen werden, unterscheidet sich dieser while-Block nur noch in einem Punkt von der letzten Version: Die neu hinzugefügte Anweisung in Zeile 18 berechnet den Wert von $maxfreq, die maximale Häufigkeit. Diese gibt an, wie oft die Zahl vorkommt, die am häufigsten aufgetaucht ist. Mit diesem Wert legen wir später die Gesamthöhe des Diagramms fest. Hier vergleichen wir die maximale Häufigkeit lediglich mit der aktuellen und verändern $maxfreq, wenn letztere größer ist.

Weiter unten im Skript - nachdem wir sortiert, summiert und die ersten Ergebnisse ausgegeben haben - kommen wir zum Histogrammteil, der etwas einschüchternden Anhäufung von Schleifen in den Zeilen 36 bis 49.

Ein Histogramm mit horizontalen Balken ist viel einfacher zu erstellen als eins mit vertikalen. Für das horizontale Histogramm brauchten wir vorgestern nur die Schlüssel im %freq-Hash zu durchlaufen und die entsprechende Zahl Sternchen auszudrucken (und das Ganze etwas zu formatieren). Für ein vertikales Histogramm müssen wir von Anfang an viel mehr Informationen über das Gesamtlayout parat haben, weil die Zeilen, die wir zeichnen, in keinem direkten Zusammenhang mit einem bestimmten Schlüssel oder Wert im Hash stehen. Außerdem müssen wir auch die Leerstellen für die Formatierung beachten.

Wir bauen das Histogramm mit zwei Schleifen. Die äußere, eine for-Schleife, kontrolliert die Anzahl der auszugebenden Zeilen, das heißt die Höhe von oben nach unten. Die zweite, eine foreach-Schleife, bewegt sich innerhalb jeder Zeile von links nach rechts und schreibt entweder ein Sternchen oder ein Leerzeichen in jede Spalte. Mit diesen beiden verschachtelten Schleifen (der for-Schleife außen und der foreach- Schleife innen) können wir uns von Zeile zu Zeile und von links nach rechts bewegen und sowohl die Höhe als auch die Breite des Histogramms von unseren Daten abhängig machen.

Zuerst erstellen wir in Zeile 34 eine sortierte Liste aller Schlüssel des %freq-Hash, vor allem aus Bequemlichkeit und damit die for-Schleifen wenigstens etwas durchschaubarer werden.

In Zeile 36 startet unsere äußere for-Schleife. Die Gesamthöhe des Diagramms wird von der am häufigsten auftretenden Zahl in unseren Daten bestimmt. Hier kommt der beim Einlesen ermittelte Wert von $maxfreq zum Einsatz. Die äußere for-Schleife beginnt bei diesem Höchstwert und arbeitet sich hinunter bis zur 0, wo sie abbricht. So erhalten wir die richtige Anzahl Zeilen.

Die innere Schleife ist für die einzelnen Zeilen zuständig. Sie durchläuft die Schlüssel des %freq-Hash (also unsere Zahlen) und überprüft, ob in der aktuellen Zeile bei dieser Zahl ein * oder ein Leerzeichen gesetzt werden muss. Wir achten auch hier auf die Formatierung und fügen für die Spalten mit mehrstelligen Zahlen entsprechend mehr Leerstellen hinzu ($space wird für eine Zahl 333 einen anderen Wert haben als für 1).

Zeile für Zeile machen wir ab Zeile 38 folgendes:

Wenn wir die Sternchen des Histogramms ausgegeben haben, fehlen nur noch die Bezeichnungen für die Spalten. Hier simulieren wir mit Bindestrichen eine Querlinie (die Anzahl der erforderlichen Bindestriche haben wir bereits mit dem Wert von $totalspace berechnet) und schreiben unsere Zahlen darunter: einen String, der die Variable @keys interpoliert und alle Elemente von @keys, durch je ein Leerzeichen getrennt, ausgibt.

Kompliziert verschachtelte Schleifen sind häufig nur schwer nachzuvollziehen. Manchmal reicht eine Erklärung wie meine einfach nicht aus. Wenn dieses Beispiel Sie auch jetzt noch verwirrt, versuchen Sie, es mit ein paar kleinen Beispielzahlen selbst »durchzuspielen«. Verfolgen Sie Schritt für Schritt und Schleife für Schleife die »Entwicklung« der verschiedenen Werte - das hilft Ihnen, die Zusammenhänge zwischen den einzelnen Variablen klarer zu erkennen.

Ein Zahlenbuchstabierer

Unser zweites Beispiel hat im wirklichen Leben nicht besonders viel Sinn, aber es zeigt ein paar komplexere Einsatzmöglichkeiten für if und while. Dieses Skript bittet Sie um die Eingabe einer einstelligen Zahl (und fängt Strings oder mehrstellige Zahlen ab). Dann schreibt es die Zahl als Wort aus, also 2 als zwei und 5 als fuenf und so weiter. Schließlich fragt es, ob Sie eine weitere Zahl eingeben möchten, und wenn ja, wiederholt es den gesamten Prozeß. Hier ein Beispiel, wie es ablaufen könnte:

% zahlenbuchstabierer.pl
Geben Sie die zu buchstabierende Zahl ein: buh
Keine Strings. Eine Zahl von 0 bis 9 bitte.
Geben Sie die zu buchstabierende Zahl ein: 45
Zu hoch. 0 bis 9 bitte.
Geben Sie die zu buchstabierende Zahl ein:: 6
6 ist sechs.
Eine weitere Zahl versuchen (j/n)?: weiss nicht
j oder n bitte.
Eine weitere Zahl versuchen (j/n)?: j
Geben Sie die zu buchstabierende Zahl ein: 3
3 ist drei.
Eine weitere Zahl versuchen (j/n)?: n

Listing 7.2 zeigt den kompletten Code.

Listing 7.2: Das Skript zahlenbuchstabierer.pl.

1:  #!/usr/bin/perl -w
2: # zahlenbuchstabierer: buchstabiert Zahlen
3: # Simple Version, nur einstellige Zahlen
4:
5: $num = 0; # Zahl
6: $exit = ""; # Programm verlassen? j oder n.
7:
8: while ($exit ne "n") {
9:
10: while () {
11: print 'Geben Sie die zu buchstabierende Zahl ein: ';
12: chomp($num = <STDIN>);
13: if ($num ne "0" && $num == 0) { # wenn $num ein String
14: print "Keine Strings. Eine Zahl von 0 bis 9 bitte.\n";
15: next;
16: }
17: if ($num > 9) { # wenn $num mehrstellige Zahl
18: print "Zu hoch. 0 bis 9 bitte.\n";
19: next;
20: }
21: if ($num < 0) { # wenn $num negative Zahl
22: print "Keine negativen Zahlen. 0 bis 9 bitte.\n";
23: next;
24: }
25: last;
26: }
27:
28: print "$num ist ";
29: if ($num == 1) { print 'eins'; }
30: elsif ($num == 2) { print 'zwei'; }
31: elsif ($num == 3) { print 'drei'; }
32: elsif ($num == 4) { print 'vier'; }
33: elsif ($num == 5) { print 'fuenf'; }
34: elsif ($num == 6) { print 'sechs'; }
35: elsif ($num == 7) { print 'sieben'; }
36: elsif ($num == 8) { print 'acht'; }
37: elsif ($num == 9) { print 'neun'; }
38: elsif ($num == 0) { print 'null'; }
39: print "\n";
40:
41: while () {
42: print 'Eine weitere Zahl versuchen (j/n)?: ';
43: chomp ($exit = <STDIN>);
44: $exit = lc $exit;
45: if ($exit ne 'j' && $exit ne 'n') {
46: print "j oder n bitte.\n";
47: }
48: else { last; }
49: }
50: }

Beginnen wir bei der äußeren Schleife, und arbeiten wir uns nach innen vor. Die erste while-Schleife, von Zeile 8 bis 50, enthält fast das gesamte Skript, denn sie soll, wenn am ja/nein-Prompt j eingegeben wird, auch fast das gesamte Skript noch einmal ausführen. Weil wir sie mindestens einmal laufen lassen möchten, muss die Bedingung in Zeile 8 am Anfang wahr sein (in weiser Voraussicht haben wir die Variable $exit dementsprechend initialisiert).

Die zweite while-Schleife, von Zeile 10 bis 26, überprüft die Benutzereingaben. Das erste if stellt sicher, dass Sie keine Strings eingegeben haben (wenn $num nicht das Zeichen 0 ist, aber im numerischen Vergleich zu 0 ausgewertet wird, muss $num ein String sein). Das zweite if sieht nach, ob Sie eine Zahl größer 9 eingegeben haben (diese Version des Skripts buchstabiert nur einstellige Zahlen), und das dritte prüft auf negative Zahlen. Wenn eine dieser Bedingungen erfüllt ist, überspringen wir mit dem Schleifensteuerbefehl next den Rest des Blocks und gehen zurück zum Anfang der Schleife, in der wir uns unmittelbar befinden - das ist in diesem Fall immer noch die endlose while-Schleife in den Zeilen 10 bis 26. Wenn die Eingabe unseren Kriterien entspricht, also kein String und eine Zahl zwischen 0 und 9 ist, dann steigen wir mit last aus dieser while-Schleife aus und machen mit der ersten ihr folgenden Anweisung weiter.

Diese Anweisung steht in Zeile 28. Wir geben den ersten Teil der Ergebnismeldung aus und gehen dann durch einen Satz von if- und elsif-Anweisungen, die für die aktuelle Zahl den entsprechenden String auf den Bildschirm schreiben. Dieser Abschnitt (Zeile 29 bis 38) wäre ein klassischer Fall für eine switch-Anweisung, mit der wir hier nicht immer wieder das gleiche eintippen müßten (den Programmiergöttern sei Dank für Copy-and-Paste).

Sobald wir unser Zahlwort ausgegeben haben, fragen wir in der letzten while-Schleife (Zeile 41 bis 49), ob der gesamte Vorgang von vorn beginnen soll. Als Antwort erwarten wir ein j oder ein n. Beachten Sie den Aufruf der Funktion lc in Zeile 44 - wenn der Benutzer Großbuchstaben eingibt, wandelt lc sie vor der Überprüfung in Kleinbuchstaben um - damit akzeptieren wir also auch J oder N, ohne explizit darauf prüfen zu müssen.

Wie Sie sehen, bestimmen wir hier nicht, was bei einem j oder n passieren soll, wir überprüfen nur, ob tatsächlich j oder n eingegeben wurde. Mehr ist gar nicht nötig. Sobald $exit einen gültigen Wert hat, endet die äußere while-Schleife, und wir kommen direkt zum Anfang, zur Schleifenbedingung in Zeile 8, zurück. Hier entscheiden wir über den weiteren Ablauf: Bei einem n bricht die äußere while- Schleife wegen nicht erfüllter Schleifenbedingung ab, und das Skript ist beendet. Anderenfalls beginnen wir den Durchlauf von vorn und machen so lange weiter, bis der Anwender ein n eingibt.

Simple Text-zu-HTML-Konvertierung

Beenden wir dieses Kapitel mit einem etwas nützlicheren Skript: webseite.pl liest eine einfache Textdatei, fragt vom Benutzer ein paar grundlegende Einstellungen ab und spuckt dann eine HTML-Version der Datei aus. Dieses Skript ist kein besonders raffinierter HTML-Generator. Es läßt Sie lediglich Vorder- und Hintergrundfarbe, eine Überschrift sowie Ihre E-Mail-Adresse vorgeben, und im Text selbst fügt es nur Absatz-Tags an den richtigen Stellen ein. Aber Sie erhalten immerhin eine grundlegende HTML-Vorlage, mit der Sie weiterarbeiten können.

Der Ablauf

Bevor es Ihren Text in HTML konvertiert, bittet das webseite-Skript Sie um ein paar Angaben:

Ein Ablauf von webseite.pl könnte zum Beispiel folgendermaßen aussehen:

% webseite.pl heine.txt
Geben Sie den Titel Ihrer Webseite ein: Heinrich Heine, Reisebilder, Dritter Teil
Geben Sie die Hintergrundfarbe ein (? zeigt Auswahl): ?
Zur Wahl stehen folgende Farben:
black (schwarz), maroon (dunkelbraun),
green (gruen), olive (olivgruen),
navy (dunkelblau), purple (violett),
teal (blaugruen), gray (grau),
silver (silbergrau), red (rot),
lime (hellgruen), yellow (gelb),
blue (blau), fuchsia (pink),
aqua (hellblau), white (weiss),
oder Return fuer keine Farbe
Geben Sie die Hintergrundfarbe ein (? zeigt Auswahl): white
Geben Sie Textfarbe ein (? zeigt Auswahl): black
Geben Sie eine Ueberschrift ein: Kapitel XXVI
Geben Sie Ihre E-Mail-Adresse ein: lemay@lne.com
******************************
<HTML>
<HEAD>
<TITLE>Heinrich Heine, Reisebilder, Dritter Teil</TITLE>
</HEAD>
<BODY BGCOLOR="white" TEXT="black">
<H1>Kapitel XXVI</H1>
<P>
Die Natur wollte wissen, wie sie aussieht, und sie erschuf Goethe.

(... und so weiter, hier aus Platzgründen nicht aufgeführt).

<P>
Es liegt Wahrheit in diesen Worten und ich bin sogar der Meinung, dass Goethe manchmal seine Sache noch besser gemacht hätte als der liebe Gott selbst und dass er z.B. den Herrn Eckermann viel richtiger, ebenfalls mit Federn und grün erschaffen hätte.
<P>
<HR>
<ADDRESS><A HREF="mailto:lemay@lne.com">lemay@lne.com</A></ADDRESS>
</BODY>
</HTML>

Der fertige HTML-Code wird von diesem Skript nur auf den Bildschirm ausgegeben - das nützt Ihnen herzlich wenig, ich weiß - und bitte um etwas Geduld. Wie Sie Daten in eine Datei statt an die Standardausgabe leiten, behandeln wir an Tag 15. Bis dahin könnten Sie den hier ausgegebenen HTML-Text mit Copy-and-Paste in einen Texteditor übernehmen und als HTML-Datei speichern. Wenn Sie diese Datei dann in einen Webbrowser laden, sehen Sie das Ergebnis von webseite.pl. Unser Beispiel ergäbe etwa folgendes Bild:

Abbildung 7.1:  Das Ergebnis des Skripts  webseite.pl

Die Eingabedatei

Eine Bemerkung zu der Textdatei, die Sie an webseite.pl zum Konvertieren übergeben: Das Skript geht davon aus, dass diese aus Absätzen besteht, die durch Leerzeilen voneinander getrennt sind. Hier zum Beispiel der Inhalt der Datei heine.txt, die ich für das Beispiel oben verwendet habe:

Die Natur wollte wissen, wie sie aussieht, und sie erschuf Goethe. Sogar die Gedanken, die Intentionen der Natur vermag er uns widerzuspiegeln, und es ist einem hitzigen Goethianer, zumal in den Hundstagen, nicht zu verargen, wenn er über die Identität der Spiegelbilder mit den Objekten selbst so sehr erstaunt, dass er dem Spiegel sogar Schöpfungskraft, die Kraft, ähnliche Objekte zu erschaffen, zutraut.

Ein Herr Eckermann hat mal ein Buch über Goethe geschrieben, worin er ganz ernsthaft versichert: Hätte der liebe Gott bei Erschaffung der Welt zu Goethe gesagt: "Lieber Goethe, ich bin jetzt gottlob fertig, ich habe jetzt alles erschaffen, bis auf die Vögel und die Bäume, und du tätest mir eine Liebe, wenn du statt meiner diese Bagatellen noch erschaffen wolltest" - so würde Goethe, ebensogut wie der liebe Gott, diese Tiere und Gewächse ganz im Geiste der übrigen Schöpfung, nämlich die Vögel mit Federn und die Bäume grün, erschaffen haben.

Es liegt Wahrheit in diesen Worten und ich bin sogar der Meinung, dass Goethe manchmal seine Sache noch besser gemacht hätte als der liebe Gott selbst und dass er z.B. den Herrn Eckermann viel richtiger, ebenfalls mit Federn und grün erschaffen hätte. Es ist wirklich ein Schöpfungsfehler, dass auf dem Kopfe des Herrn Eckermann keine grünen Federn wachsen, und Goethe hat diesem Mann wenigstens dadurch abzuhelfen gesucht, dass er ihm einen Doktorhut aus Jena verschrieben und eigenhändig aufgesetzt hat.

Ich habe hier ausnahmsweise auf die ASCII-sichere Umschreibung von Umlauten und ß verzichtet - schließlich geht es bei Webseiten nicht zuletzt um die Ästhetik der Darstellung. Am Tag 1 habe ich erklärt, warum Ihr Perl-Interpreter damit nicht ohne weiteres umgehen kann. Wenn Sie diese Textdatei in dem Zeichensatz speichern, den Ihr Perl-Interpeter voraussetzt, werden Sie innerhalb Ihres Systems erst einmal keine Schwierigkeiten haben - beachten Sie jedoch, dass diese Datei auf dem Rechner Ihrer Brieffreundin in Kopenhagen (oder auch nur bei Ihrem Nachbarn, mit dessen Betriebssystem Sie nie arbeiten würden) zu ganz anderen, unerwünschten Ergebnissen führen kann. Übrigens haben Umlaute auch in HTML-Code eigentlich nichts zu.

Das Skript

Listing 7.3 zeigt den Code für unser Skript.

Listing 7.3: webseite.pl

1:  #!/usr/bin/perl -w
2: #
3: # webseite: simple Textdatei-zu HTML-Konvertierung
4: # *sehr* simpel. Keine Sonderzeichen, Links, Fett- oder
5: # andere Formatierungen, etc.
6: # Leerzeile in Textdatei == Absatz in HTML-Code
7:
8: $title = ''; # <TITLE>, Titel
9: $bgcolor = ''; # BGCOLOR, Hintergrundfarbe
10: $text = ''; # TEXT, Textfarbe
11: $head = ''; # <H1>, erste Ueberschrift
12: $mail = ''; # E-Mail-Adresse
13:
14: print " Geben Sie den Titel Ihrer Webseite ein: ";
15: chomp($title = <STDIN>);
16:
17: foreach $farbe ('Hintergrund', 'Text') { # laeuft zweimal: einmal
18: # je Farbe
19: $in = ''; # temp. Eingabe
20: while () {
21: print "Geben Sie die ${farbe}farbe ein (? zeigt Auswahl): ";
22: chomp($in = <STDIN>);
23: $in = lc $in;
24:
25: if ($in eq '?') { # Hilfe anzeigen
26: print "\nZur Wahl stehen folgende Farben:\n";
27: print "black (schwarz), maroon (dunkelbraun),\n";
28: print "green (gruen), olive (olivgruen),\n";
29: print "navy (dunkelblau), purple (violett),\n";
30: print "teal (blaugruen), gray (grau),\n";
31: print "silver (silbergrau), red (rot),\n";
32: print "lime (hellgruen), yellow (gelb),\n";
33: print "blue (blau), fuchsia (pink),\n";
34: print "aqua (hellblau), white (weiss),\n";
35: print "oder Return fuer keine Farbe\n\n";
36: next;
37: } elsif ($in eq '' or
38: $in eq 'black' or
39: $in eq 'maroon' or
40: $in eq 'green' or
41: $in eq 'olive' or
41: $in eq 'navy' or
42: $in eq 'purple' or
43: $in eq 'teal' or
44: $in eq 'gray' or
45: $in eq 'silver' or
46: $in eq 'red' or
47: $in eq 'lime' or
48: $in eq 'yellow' or
49: $in eq 'blue' or
50: $in eq 'fuchsia' or
51: $in eq 'aqua' or
52: $in eq 'white') { last; }
53: else {
54: print "Das ist kein gueltiger Farbname.\n";
55: }
56: }
57:
58: if ($farbe eq 'Hintergrund') { # Hintergrundfarbe
59: $bgcolor = $in;
60: } else { # Textfarbe
61: $text = $in;
62: }
63: }
64:
65: print "Geben Sie eine Ueberschrift ein: ";
66: chomp($head = <STDIN>);
67:
68: print "Geben Sie Ihre E-Mail-Adresse ein: ";
69: chomp($mail = <STDIN>);
70:
71: print '*' x 30;
72: # jetzt geht der HTML-Code los:
73: print "\n<HTML>\n<HEAD>\n<TITLE>$title</TITLE>\n";
74: print "</HEAD>\n<BODY";
75: if ($bgcolor ne '') { print qq( BGCOLOR="$bgcolor"); }
76: if ($text ne '') { print qq( TEXT="$text"); }
77: print ">\n";
78: print "<H1>$head</H1>\n<P>";
79:
80: while (<>) {
81: if ($_ eq "\n") {
82: print "<p>\n";
83: } else {
84: print $_;
85: }
86: }
87:
88: print qq(<HR>\n
89: <ADDRESS><A HREF="mailto:$mail">$mail</A></ADDRESS>\n);
90: print "</BODY>\n</HTML>\n";

Dieses Skript ist nicht besonders komplex oder syntaktisch raffiniert. Es verwendet nicht einmal Arrays oder Hashes (Wofür auch? Es gibt nichts, was hier gespeichert oder berechnet werden müßte) - nur viele, viele Schleifen und Bedingungen.

Betrachten wir zuerst die große foreach-Schleife, die in Zeile 17 beginnt. Diese Schleife ist zuständig für die Frage nach Hintergrund- und Textfarbe. Weil sich diese beiden Eingabeaufforderungen exakt gleich verhalten, wollte ich denselben Code nicht für jede einzelne Aufforderung wiederholen (insbesondere, weil die if-Bedingungen in Zeile 37 bis 53 wirklich reichlich sind). Später werden Sie lernen, wie man sich derartig wiederholenden Code in eine Subroutine packt und dann lediglich diese Subroutine zweimal aufruft. Doch jetzt, wo wir viel über Schleifen, aber nichts über Subroutinen wissen, habe ich mich für eine foreach-Schleife entschieden.

Die Schleife wird zweimal durchlaufen, einmal für den String 'Hintergrund' und einmal für den String 'Text'. Wir benutzen diese Strings für die Eingabeaufforderung und später zur Zuweisung der eingegebenen Werte an die entsprechende Variable ($bgcolor oder $text).

Innerhalb der foreach-Schleife haben wir eine andere, eine unendliche while- Schleife, die die Eingabeaufforderungen so lange wiederholt, bis wir akzeptable Eingaben haben (Fehlerbehandlung ist immer eine gute Programmierübung). Der Benutzer kann 18 verschiedene Dinge eingeben: eine der sechzehn Grundfarben, ein Fragezeichen oder gar nichts.

Die Bedingungen in Zeile 25 bis 55 durchlaufen jede dieser Möglichkeiten, zuerst die Eingabe von ?. Als Antwort auf ein Fragezeichen müssen wir lediglich eine hilfreiche Mitteilung ausgeben und dann mit next zum nächsten Durchlauf der while-Schleife springen (also, die Eingabeaufforderung neu anzeigen und auf weitere Eingaben warten).

Die nächste Überprüfung (ab Zeile 37) stellt sicher, dass wir korrekte Eingaben haben, nämlich entweder einen Zeilenvorschub (dann haben wir eine leere Eingabe und legen keine Farbe fest1) oder eine der sechzehn Grundfarben. Beachten Sie, dass hier immer auf kleingeschriebene Farbnamen überprüft wird. In Zeile 23 haben wir mit der Funktion lc die Eingabestrings komplett in Kleinbuchstaben umgewandelt; deswegen kann der Benutzer auch BLACK oder Black eingeben - wir haben alle Möglichkeiten zu einer zusammengefaßt (aber bequemerweise die Eingabe von ? nicht berührt).

Wenn die Eingabe einer dieser Vorgaben entspricht, steigen wir in Zeile 52 mit last aus der while-Schleife aus (Sie erinnern sich, next und last beziehen sich ohne Labels auf die nächste sie umgebende Schleife - hier also auf die while-, nicht auf die foreach-Schleife). Wenn uns die Eingabe nicht paßt, springen wir zum letzten else- Fall in Zeile 53, geben eine Fehlermeldung aus und beginnen die while-Schleife von vorn.

Die letzte Überprüfung in der foreach-Schleife (Zeile 58 bis 62) stellt fest, ob es sich bei der Eingabe um den Wert für die Hintergrund- oder die Textfarbe handelt, und weist den Farbwert dann der entsprechenden Variablen zu.

Der Rest des Skripts, von Zeile 73 bis zum Ende, gibt den Anfang unserer HTML-Datei aus, liest und konvertiert die in der Kommandozeile angegebene Textdatei und setzt die abschließenden HTML-Tags ans Ende. Beachten Sie die Überprüfungen in Zeile 75 und 76: Wenn $bgcolor und $text keine Werte haben, schreiben wir diese Attribute gar nicht erst in den HTML-Tag <BODY>. (Einfacher wäre es, sie dort stehen zu lassen, BGCOLOR="" oder TEXT="", aber das hätte nicht besonders nett ausgesehen.)

Beachten Sie auch die Verwendung der Funktion qq. Sie haben qq an Tag 2 im Vertiefungsabschnitt kennengelernt. Mit der qq-Funktion erstellt man einen double- quoted String, ohne doppelte Anführungszeichen zu verwenden. Wenn ich hier doppelte Anführungszeichen verwendet hätte, hätte ich vor die Anführungszeichen im String selbst einen Backslash setzen müssen. Ich finde, so sieht es besser aus.

Die Zeilen 80 bis 86 lesen (mit <>) die Textdatei und geben dann einfach alles wieder aus, nur mit Absatz-Tags (<P>) statt Leerzeilen. Eine robustere Version dieses Skripts würde nach Sonderzeichen suchen (Akzenten, Umlauten und so weiter) und sie durch ihre entsprechenden HTML-Codes ersetzen - aber diese Aufgabe läßt sich mit Pattern Matching (Mustervergleich) viel einfacher lösen; deswegen heben wir uns das für später auf.

Zum Abschluß bleibt nur noch die Ausgabe des E-Mail-Links (mit einem HTML mailto und Link-Tags) und der Tags zum Beenden der HTML-Datei (</BODY></HTML>).

Zusammenfassung

Programmierlehrbüchern mangelt es selten an wortreichen Erklärungen, aber häufig an konkreten Beispielen. Ich möchte mich nicht um ausführliche Erklärungen drücken (wer lacht da?), aber diese Lektion - und die zwei weiteren an den Tagen 14 und 21 - zeigt Ihnen etwas längeren Code, der ohne thematische Trennung die Techniken aus den vorangegangenen Lektionen anwendet und vielleicht sogar eine mehr oder weniger nützliche Aufgabe erfüllt (obwohl ich nicht sicher bin, wie oft Sie eine Zahl werden buchstabieren müssen).

Wir haben uns heute drei Skripts angesehen: statsfinal.pl, eine weitere Neuauflage des vertrauten Statistik-Skripts, gab - nach sorgfältigem Maßnehmen - mit Hilfe zweier verschachtelter for-Schleifen ein vertikales Histogramm aus. Das zweite, zahlenbuchstabierer.pl, arbeitete mit sehr vielen Bedingungen, um schließlich eine einstellige Zahl zu buchstabieren. Das dritte, webseite.pl, nahm in der Kommandozeile eine Textdatei entgegen, fragte nach ein paar Angaben und konvertierte den Inhalt der Textdatei in eine Webseite.

Herzlichen Glückwunsch, Sie haben die erste Woche dieses Drei-Wochen-Buches und bereits ein mächtiges Stück der Sprache bewältigt. Darauf bauen wir weiter auf. Auf zur Woche 2!



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


1

Die Farbwahl bleibt dann den Standardeinstellungen des Webbrowsers überlassen.

© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH